package com.jwater.console.service.impl;

import com.jwater.console.model.TreeNode;
import com.jwater.console.service.ConsoleService;
import com.jwater.core.Bootstrap;
import com.jwater.core.common.IpPort;
import com.jwater.core.common.S;
import com.jwater.core.common.param.ParamMap;
import com.jwater.core.manager.common.FileInfoUtils;
import com.jwater.core.manager.common.JvmUtils;
import com.jwater.core.manager.common.ListLogUtils;
import com.jwater.core.manager.common.MsgKeys;
import com.jwater.core.manager.common.conf.CoreConfParse;
import com.jwater.core.manager.common.conf.WorkerConfParse;
import com.jwater.core.manager.common.deploy.DeployUtils;
import com.jwater.core.manager.common.model.*;
import com.jwater.core.manager.common.service.JwaterService;
import com.jwater.core.manager.proxy.ProxyClient;
import com.jwater.core.manager.proxy.ProxyManager;
import com.jwater.core.metrics.SystemMetric;
import com.jwater.core.net.Node;
import com.jwater.core.net.SimpleNode;
import com.jwater.core.net.group.GroupExecutorUtils;
import com.jwater.core.net.group.Member;
import com.jwater.core.utils.JSchUtils;
import com.jwater.core.utils.SysType;
import com.jwater.launch.DeployPathUtils;
import com.jwater.launch.HomePath;
import jwater.org.apache.commons.io.FileUtils;
import jwater.org.apache.commons.io.IOUtils;
import jwater.org.springframework.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;

/**
 * @description:
 * @author: jnner
 * @createDate: 2021/2/5
 * @version: 1.0
 */
@Service
public class ConsoleServiceImpl implements ConsoleService {
    protected static Logger logger = LoggerFactory.getLogger(ConsoleServiceImpl.class);
    // jwater.core.xml文件解析器
    private CoreConfParse coreConfParse;
    // 连接管理进程的client
    private Node clientNode;
    // 与管理进程的rpc服务调用
    private JwaterService jwaterService;
    // 与代理进程通信的管理器
    private ProxyManager proxyManager;
    // 登录用户的token信息
    private final Map<String, Long> tokens = new ConcurrentHashMap<>();
    // 测试时设置其他值
    private final boolean useLocal = true;

    /**
     * 初始化与管理进程和代理进程的通信
     */
    @PostConstruct
    public void init() {
        logger.info("init begin");
        // 解析配置
        coreConfParse = new CoreConfParse();
        coreConfParse.parse();
        // 与管理进程建立连接
        clientNode = new SimpleNode();
        clientNode.startAsClient(coreConfParse.getMasterHosts(), true);
        // 获取rpc调用对象
        jwaterService = clientNode.getProxy(JwaterService.class);
        // 与代理进程建立连接
        proxyManager = new ProxyManager(coreConfParse.getProxyInfos());
        proxyManager.start(coreConfParse);
        logger.info("init end");
    }

    /**
     * 检查与管理进程的连接是否连着的
     */
    public boolean checkConnect() {
        if (clientNode == null || !clientNode.isConnect()) {
            return false;
        }
        return true;
    }

    /**
     * 重启管理进程和代理进程的连接信息
     *
     * @param changeProxyInfos
     */
    private void reboot(Map<String, ProxyInfo> changeProxyInfos) {
        if (isChangeClientNode()) {
            clientNode.close();
            clientNode = new SimpleNode();
            clientNode.startAsClient(coreConfParse.getMasterHosts(), true);
            jwaterService = clientNode.getProxy(JwaterService.class);
        }
        proxyManager.reboot(this.coreConfParse, changeProxyInfos);
    }

    /**
     * 判断jwater.core.xml里的管理进程跟实际连的client是否一致
     *
     * @return
     */
    private boolean isChangeClientNode() {
        List<IpPort> masterHosts = coreConfParse.getMasterHosts();
        for (IpPort masterHost : masterHosts) {
            if ((masterHost.ip + ":" + masterHost.port).equals(this.clientNode.getNodeId())) {
                return false;
            }
        }
        return true;
    }

    /**
     * 从新的配置jwater.core.xml中获取已经改变的代理进程配置信息
     *
     * @return
     */
    private Map<String, ProxyInfo> getChangeProxyInfos() {
        Map<String, ProxyInfo> changeProxyInfos = new HashMap<>();
        CoreConfParse confParse = new CoreConfParse();
        confParse.parse();
        List<ProxyInfo> proxyInfos = confParse.getProxyInfos();
        for (ProxyInfo proxyInfo : proxyInfos) {
            if (!existProxyInfo(proxyInfo, confParse.getNodeInfo(proxyInfo.getHost()))) {
                changeProxyInfos.put(proxyInfo.getHost(), proxyInfo);
            }
        }
        this.coreConfParse = confParse;
        return changeProxyInfos;
    }

    /**
     * 获取新的代理信息在老的配置里是否存在
     *
     * @param newProxyInfo
     * @param newNodeInfo
     * @return
     */
    private boolean existProxyInfo(ProxyInfo newProxyInfo, NodeInfo newNodeInfo) {
        List<ProxyInfo> proxyInfos = coreConfParse.getProxyInfos();
        for (ProxyInfo proxyInfo : proxyInfos) {
            NodeInfo nodeInfo = coreConfParse.getNodeInfo(proxyInfo.getHost());
            if (newProxyInfo.getHost().equals(proxyInfo.getHost())
                    && newProxyInfo.getPort() == proxyInfo.getPort()
                    && newNodeInfo.getUsername().equals(nodeInfo.getUsername())
                    && newNodeInfo.getPassword().equals(nodeInfo.getPassword())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 登录认证
     *
     * @param regionName
     * @param username
     * @param password
     * @param token
     * @return
     * @throws Exception
     */
    @Override
    public boolean login(String regionName, String username, String password, String token) throws Exception {
        String pw = coreConfParse.getCoreConfigInfo().getLogins().get(regionName + "," + username);
        if (!StringUtils.isEmpty(pw) && pw.equals(password)) {
            tokens.put(token, System.currentTimeMillis());
            return true;
        }
        return false;
    }

    /**
     * 退出登录
     *
     * @param token
     * @throws Exception
     */
    @Override
    public void logout(String token) throws Exception {
        tokens.remove(token);
    }

    /**
     * 是否存在token
     *
     * @param token
     * @return
     * @throws Exception
     */
    @Override
    public boolean existToken(String token) throws Exception {
        return tokens.containsKey(token);
    }

    /**
     * 获取配置文件列表
     *
     * @return
     */
    @Override
    public List<String> getConfList() {
        List<String> list = new ArrayList<String>();
        String dir = HomePath.getConfPath();
        listFiles(list, new File(dir), true);
        return list;
    }

    /**
     * 罗列目录下所有的文件
     *
     * @param list
     * @param dir
     * @param recursive
     */
    private void listFiles(List<String> list, File dir, boolean recursive) {
        File[] files = dir.listFiles();
        if (files == null) {
            return;
        }
        for (File file : files) {
            if (file.isDirectory()) {
                if (recursive) {
                    listFiles(list, file, recursive);
                }
            } else {
                String p = file.getPath().replace("\\", "/");
                if (!list.contains(p)) {
                    list.add(p);
                }
            }
        }
    }

    /**
     * 获取配置内容
     *
     * @param path
     * @return
     * @throws Exception
     */
    @Override
    public String getConfContent(String path) throws Exception {
        InputStream input = null;
        try {
            File file = new File(path);
            if (file.exists()) {
                input = new FileInputStream(file);
                return IOUtils.toString(input, S.charset);
            }
        } finally {
            IOUtils.closeQuietly(input);
        }
        return "";
    }

    /**
     * 保存配置内容
     *
     * @param path
     * @param content
     * @throws Exception
     */
    @Override
    public void saveConfContent(String path, String content) throws Exception {
        if (path.endsWith("jwater.core.xml")) {
            OutputStream output = new FileOutputStream(path);
            IOUtils.write(content.getBytes(S.charset), output);
            output.close();
            Map<String, ProxyInfo> changeProxyInfos = getChangeProxyInfos();
            reboot(changeProxyInfos);
        }
        List<NodeInfo> nodeInfos = coreConfParse.getNodeList();
        for (NodeInfo nodeInfo : nodeInfos) {
            if (useLocal && SystemMetric.isLocalHost(nodeInfo.getHost())) {
                OutputStream output = new FileOutputStream(path);
                IOUtils.write(content.getBytes(S.charset), output);
                output.close();
            } else {
                String remotePath = HomePath.getRemotePath(path, nodeInfo.getPath());
                ProxyClient proxyClient = proxyManager.getProxyClient(nodeInfo.getHost());
                if (proxyClient != null && proxyClient.isConnect()) {
                    proxyClient.saveContent(remotePath, content);
                    logger.info("save to " + nodeInfo.getHost() + " success for " + path);
                } else {
                    if ("true".equals(nodeInfo.getSsh()) && SysType.LINUX.equals(nodeInfo.getType())) {
                        InputStream src = new ByteArrayInputStream(content.getBytes(S.charset));
                        JSchUtils.putFile(nodeInfo.getHost(), nodeInfo.getPort(), nodeInfo.getUsername(),
                                nodeInfo.getPassword(), src, remotePath);
                        IOUtils.closeQuietly(src);
                        logger.info("save to " + nodeInfo.getHost() + " success for " + path);
                    } else {
                        logger.error("proxy not online,save to " + nodeInfo.getHost() + " error for " + path);
                    }
                }
            }
        }
    }

    /**
     * 新建一个配置文件
     *
     * @param sys
     * @param regionName
     * @param deployName
     * @param fileName
     * @throws Exception
     */
    @Override
    public void newConf(boolean sys, String regionName, String deployName, String fileName) throws Exception {
        File file = null;
        if (sys) {
            file = new File(HomePath.getHomePath(), fileName);
        } else {
            file = new File(HomePath.getRegion(), fileName);
        }
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        OutputStream output = new FileOutputStream(file);
        IOUtils.write("".getBytes(), output);
        IOUtils.closeQuietly(output);
    }

    /**
     * 删除配置文件
     *
     * @param path
     * @throws Exception
     */
    @Override
    public void deleteConf(String path) throws Exception {
        List<NodeInfo> nodeInfos = coreConfParse.getNodeList();
        for (NodeInfo nodeInfo : nodeInfos) {
            if (useLocal && SystemMetric.isLocalHost(nodeInfo.getHost())) {
                FileUtils.deleteQuietly(new File(path));
            } else {
                String remotePath = HomePath.getRemotePath(path, nodeInfo.getPath());
                ProxyClient proxyClient = proxyManager.getProxyClient(nodeInfo.getHost());
                if (proxyClient != null && proxyClient.isConnect()) {
                    proxyClient.removeFile(remotePath);
                    logger.info("delete to " + nodeInfo.getHost() + " success for " + remotePath);
                } else {
                    if ("true".equals(nodeInfo.getSsh()) && SysType.LINUX.equals(nodeInfo.getType())) {
                        JSchUtils.removeFile(nodeInfo.getHost(), nodeInfo.getPort(), nodeInfo.getUsername(),
                                nodeInfo.getPassword(), remotePath);
                        logger.info("delete to " + nodeInfo.getHost() + " success for " + remotePath);
                    } else {
                        logger.error("proxy not online,delete conf " + nodeInfo.getHost() + " error for " + remotePath);
                    }
                }
            }
        }
    }

    /**
     * 获取管理进程列表
     *
     * @return
     * @throws Exception
     */
    @Override
    public List<MasterInfo> getMasterList() throws Exception {
        List<MasterInfo> masterInfos = new ArrayList<>();
        List<Member> members = coreConfParse.getMembers();
        if (members.isEmpty()) {
            return masterInfos;
        }
        CountDownLatch latch = new CountDownLatch(members.size());
        for (Member member : members) {
            MasterInfo masterInfo = new MasterInfo();
            masterInfo.setIndex(member.getMemberId());
            masterInfo.setHost(member.getIp());
            masterInfo.setInnerPort(member.getInnerPort());
            masterInfo.setOuterPort(member.getOuterPort());
            masterInfo.setHomePath(HomePath.getRemoteHomePath(member.getNodeInfo().getPath()));
            masterInfos.add(masterInfo);
            if (clientNode.getNodeId().equals(member.getIp() + ":" + member.getOuterPort())) {
                masterInfo.setOnline(clientNode.isConnect());
                setUptime(clientNode, masterInfo);
                latch.countDown();
            } else {
                GroupExecutorUtils.execute(() -> {
                    SimpleNode client = new SimpleNode();
                    try {
                        client.setAutoConnect(false);
                        client.setConnectInterval(0);
                        client.setConnectLimit(1);
                        client.startAsClient(member.getIp(), member.getOuterPort(), false);
                        masterInfo.setOnline(client.isConnect());
                        setUptime(client, masterInfo);
                    } catch (Exception e) {
                        logger.error(e.toString(), e);
                    } finally {
                        latch.countDown();
                        client.close();
                    }
                });
            }
        }
        latch.await();
        return masterInfos;
    }

    /**
     * 获取管理进程的启动时间
     *
     * @param client
     * @param masterInfo
     */
    private void setUptime(Node client, MasterInfo masterInfo) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            if (client.isConnect()) {
                byte[] bytes = client.syncCall(MsgKeys.GET_UPTIME, new byte[0], 2);
                long uptime = Long.parseLong(S.newString(bytes));
                if (uptime != 0) {
                    masterInfo.setUptime(sdf.format(uptime));
                }
            }
        } catch (Exception e) {
            logger.warn(e.toString());
        }
    }

    /**
     * 重启控制台
     *
     * @throws Exception
     */
    @Override
    public void rebootConsole() throws Exception {
        Bootstrap.startConsole(false);
    }

    /**
     * 启动管理进程
     *
     * @param index
     * @throws Exception
     */
    @Override
    public void startMaster(int index) throws Exception {
        Member member = coreConfParse.getMember(index);
        if (member == null) {
            logger.error("can not find master for " + index);
            return;
        }
        NodeInfo nodeInfo = member.getNodeInfo();
        if (useLocal && SystemMetric.isLocalHost(member.getIp())) {
            Bootstrap.startMaster(index, false);
        } else {
            if ("true".equals(nodeInfo.getSsh()) && SysType.LINUX.equals(nodeInfo.getType())) {
                String cmd = HomePath.getRemoteBinFile(nodeInfo.getPath(), SysType.LINUX) + " start master " + index;
                JSchUtils.exec(nodeInfo.getHost(), nodeInfo.getPort(), nodeInfo.getUsername(), nodeInfo.getPassword(),
                        cmd);
            } else {
                ProxyClient proxyClient = proxyManager.getProxyClient(nodeInfo.getHost());
                if (proxyClient != null && proxyClient.isConnect()) {
                    proxyClient.startMaster(index);
                } else {
                    logger.error("proxy not online,start master " + nodeInfo.getHost() + " error for " + index);
                }
            }
        }
    }

    /**
     * 停止管理进程
     *
     * @param index
     * @throws Exception
     */
    @Override
    public void stopMaster(int index) throws Exception {
        Member member = coreConfParse.getMember(index);
        if (member == null) {
            logger.error("can not find master for " + index);
            return;
        }
        NodeInfo nodeInfo = member.getNodeInfo();
        if (useLocal && SystemMetric.isLocalHost(member.getIp())) {
            Bootstrap.stopMaster(index);
        } else {
            if ("true".equals(nodeInfo.getSsh()) && SysType.LINUX.equals(nodeInfo.getType())) {
                String cmd = HomePath.getRemoteBinFile(nodeInfo.getPath(), SysType.LINUX) + " stop master " + index;
                JSchUtils.exec(nodeInfo.getHost(), nodeInfo.getPort(), nodeInfo.getUsername(), nodeInfo.getPassword(),
                        cmd);
            } else {
                ProxyClient proxyClient = proxyManager.getProxyClient(nodeInfo.getHost());
                if (proxyClient != null && proxyClient.isConnect()) {
                    proxyClient.stopMaster(index);
                } else {
                    logger.error("proxy not online,stop master " + nodeInfo.getHost() + " error for " + index);
                }
            }
        }
    }

    /**
     * 获取主机列表
     *
     * @return
     * @throws Exception
     */
    @Override
    public List<HostInfo> getHostList() throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<HostInfo> hostInfos = new ArrayList<HostInfo>();
        List<NodeInfo> nodeList = coreConfParse.getNodeList();
        for (NodeInfo node : nodeList) {
            ProxyClient proxyClient = proxyManager.getProxyClient(node.getHost());
            HostInfo hostInfo = new HostInfo();
            hostInfo.setHost(node.getHost());
            hostInfo.setSsh("true".equals(node.getSsh()));
            hostInfo.setType(node.getType());
            hostInfo.setHomePath(HomePath.getRemoteHomePath(node.getPath()));
            if (proxyClient == null) {
                hostInfo.setExistProxy(false);
                hostInfo.setProxyOnline(false);
                hostInfo.setUptime("");
            } else {
                hostInfo.setExistProxy(true);
                hostInfo.setProxyOnline(proxyClient.isConnect());
                if (proxyClient.getUptime() == 0) {
                    hostInfo.setUptime("");
                } else {
                    hostInfo.setUptime(sdf.format(new Date(proxyClient.getUptime())));
                }
            }
            hostInfos.add(hostInfo);
        }
        return hostInfos;
    }

    /**
     * 启动代理进程
     *
     * @param host
     * @throws Exception
     */
    @Override
    public void startProxy(String host) throws Exception {
        NodeInfo nodeInfo = coreConfParse.getNodeInfo(host);
        if (nodeInfo == null) {
            logger.error("start proxy error,can not find nodeinfo for " + host);
            return;
        }
        if (useLocal && SystemMetric.isLocalHost(nodeInfo.getHost())) {
            Bootstrap.startProxy(false);
        } else {
            if ("true".equals(nodeInfo.getSsh()) && SysType.LINUX.equals(nodeInfo.getType())) {
                String cmd = HomePath.getRemoteBinFile(nodeInfo.getPath(), SysType.LINUX) + " start proxy ";
                JSchUtils.exec(nodeInfo.getHost(), nodeInfo.getPort(), nodeInfo.getUsername(), nodeInfo.getPassword(),
                        cmd);
            } else {
                ProxyClient proxyClient = proxyManager.getProxyClient(nodeInfo.getHost());
                if (proxyClient != null && proxyClient.isConnect()) {
                    proxyClient.startProxy();
                } else {
                    logger.error("proxy not online,start proxy " + nodeInfo.getHost() + " error");
                }
            }
        }
    }

    /**
     * 停止代理进程
     *
     * @param host
     * @throws Exception
     */
    @Override
    public void stopProxy(String host) throws Exception {
        NodeInfo nodeInfo = coreConfParse.getNodeInfo(host);
        if (nodeInfo == null) {
            logger.error("stop proxy error,can not find nodeinfo for " + host);
            return;
        }
        if (useLocal && SystemMetric.isLocalHost(nodeInfo.getHost())) {
            Bootstrap.stopProxy();
        } else {
            if ("true".equals(nodeInfo.getSsh()) && SysType.LINUX.equals(nodeInfo.getType())) {
                String cmd = HomePath.getRemoteBinFile(nodeInfo.getPath(), SysType.LINUX) + " stop proxy ";
                JSchUtils.exec(nodeInfo.getHost(), nodeInfo.getPort(), nodeInfo.getUsername(), nodeInfo.getPassword(),
                        cmd);
            } else {
                ProxyClient proxyClient = proxyManager.getProxyClient(nodeInfo.getHost());
                if (proxyClient != null && proxyClient.isConnect()) {
                    proxyClient.stopProxy();
                } else {
                    logger.error("proxy not online,stop proxy " + nodeInfo.getHost() + " error");
                }
            }
        }
    }

    /**
     * 获取菜单列表
     *
     * @param regionName
     * @return
     * @throws Exception
     */
    @Override
    public String getMenuList(String regionName) throws Exception {
        File file = new File(HomePath.getDataMenuPath(), regionName + ".menu");
        if (file.exists()) {
            FileInputStream input = new FileInputStream(file);
            String content = IOUtils.toString(input, S.charset);
            input.close();
            return content;
        }
        return "";
    }

    /**
     * 保存菜单内容
     *
     * @param regionName
     * @param content
     * @throws Exception
     */
    @Override
    public void saveMenu(String regionName, String content) throws Exception {
        List<NodeInfo> nodeInfos = coreConfParse.getNodeList();
        String path = HomePath.getDataMenuPath() + "/" + regionName + ".menu";
        for (NodeInfo nodeInfo : nodeInfos) {
            if (useLocal && SystemMetric.isLocalHost(nodeInfo.getHost())) {
                File file = new File(path);
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }
                OutputStream output = new FileOutputStream(path);
                IOUtils.write(content.getBytes(S.charset), output);
                output.close();
            } else {
                String remotePath = HomePath.getRemotePath(path, nodeInfo.getPath());
                ProxyClient proxyClient = proxyManager.getProxyClient(nodeInfo.getHost());
                if (proxyClient != null && proxyClient.isConnect()) {
                    proxyClient.saveContent(remotePath, content);
                    logger.info("save to " + nodeInfo.getHost() + " success for " + path);
                } else {
                    if ("true".equals(nodeInfo.getSsh()) && SysType.LINUX.equals(nodeInfo.getType())) {
                        InputStream src = new ByteArrayInputStream(content.getBytes(S.charset));
                        JSchUtils.putFile(nodeInfo.getHost(), nodeInfo.getPort(), nodeInfo.getUsername(),
                                nodeInfo.getPassword(), src, remotePath);
                        IOUtils.closeQuietly(src);
                        logger.info("save to " + nodeInfo.getHost() + " success for " + path);
                    } else {
                        logger.error("proxy not online,save to " + nodeInfo.getHost() + " error for " + path);
                    }
                }
            }
        }
    }

    /**
     * 获取部署路径
     *
     * @param regionName
     * @return
     * @throws Exception
     */
    @Override
    public List<String> getDeployList(String regionName) throws Exception {
        List<String> list = new ArrayList<String>();
        File dir = new File(HomePath.getRegionDeploy(regionName));
        File[] files = dir.listFiles();
        if (files == null) {
            return list;
        }
        for (File file : files) {
            list.add(file.getName());
        }
        return list;
    }

    /**
     * 部署安装包
     *
     * @param path
     * @param regionName
     * @param deployName
     * @param keepConf
     * @param fileName
     * @param tmpFile
     * @throws Exception
     */
    @Override
    public void deploy(String path, String regionName, String deployName, String keepConf, String fileName,
                       File tmpFile) throws Exception {
        try {
            List<NodeInfo> nodeInfos = coreConfParse.getNodeList();
            long version = System.currentTimeMillis();
            for (NodeInfo nodeInfo : nodeInfos) {
                if (useLocal && SystemMetric.isLocalHost(nodeInfo.getHost())) {
                    String dst = HomePath.getRemoteInstallFilePath(HomePath.getHomePath(), regionName, fileName);
                    FileUtils.copyFile(tmpFile, new File(dst));
                    DeployUtils.deploy(fileName, dst, regionName, deployName, keepConf, version, path);
                } else {
                    String homePath = HomePath.getRemoteHomePath(nodeInfo.getPath());
                    String dst = HomePath.getRemoteInstallFilePath(homePath, regionName, fileName);
                    ProxyClient proxyClient = proxyManager.getProxyClient(nodeInfo.getHost());
                    if (proxyClient != null && proxyClient.isConnect()) {
                        FileInputStream fis = new FileInputStream(tmpFile);
                        proxyClient.putFile(fis, dst);
                        IOUtils.closeQuietly(fis);
                        proxyClient.deploy(fileName, dst, regionName, deployName, keepConf, version, path);
                        logger.info("put deploy file " + nodeInfo.getHost() + " success for " + fileName);
                    } else {
                        logger.error(
                                "proxy not online,put deploy file " + nodeInfo.getHost() + " error for " + fileName);
                    }
                }
            }
        } finally {
            FileUtils.deleteQuietly(tmpFile);
        }
    }

    /**
     * 移除安装包
     *
     * @param regionName
     * @param deployName
     * @throws Exception
     */
    @Override
    public void removeDeploy(String regionName, String deployName) throws Exception {
        List<NodeInfo> nodeInfos = coreConfParse.getNodeList();
        for (NodeInfo nodeInfo : nodeInfos) {
            if (useLocal && SystemMetric.isLocalHost(nodeInfo.getHost())) {
                DeployUtils.removeDeploy(regionName, deployName);
            } else {
                ProxyClient proxyClient = proxyManager.getProxyClient(nodeInfo.getHost());
                if (proxyClient != null && proxyClient.isConnect()) {
                    proxyClient.removeDeploy(regionName, deployName);
                    logger.info("remove deploy " + nodeInfo.getHost() + " success for " + deployName);
                } else {
                    logger.error("proxy not online,remove deploy " + nodeInfo.getHost() + " error for " + deployName);
                }
            }
        }
    }

    /**
     * 获取管理进程列表
     *
     * @param regionName
     * @return
     * @throws Exception
     */
    @Override
    public List<WorkerInfo> getWorkerList(String regionName) throws Exception {
        return jwaterService.getWorkerList(regionName);
    }

    /**
     * 启动工作进程
     *
     * @param regionName
     * @param deployName
     * @param groupName
     * @param workerName
     * @param index
     * @throws Exception
     */
    @Override
    public void startWorker(String regionName, String deployName, String groupName, String workerName, int index)
            throws Exception {
        jwaterService.startWorker(regionName, deployName, groupName, workerName, index);
    }

    /**
     * 停止工作进程
     *
     * @param regionName
     * @param deployName
     * @param groupName
     * @param workerName
     * @param index
     * @throws Exception
     */
    @Override
    public void stopWorker(String regionName, String deployName, String groupName, String workerName, int index,
                           boolean isForce)
            throws Exception {
        jwaterService.stopWorker(regionName, deployName, groupName, workerName, index, isForce);
    }

    /**
     * 获取工作进程配置列表
     *
     * @param regionName
     * @param deployName
     * @return
     * @throws Exception
     */
    @Override
    public List<String> getWorkerConfList(String regionName, String deployName) throws Exception {
        return jwaterService.getWorkerConfList(regionName, deployName);
    }

    /**
     * 获取工作进程配置的根目录
     *
     * @param regionName
     * @param deployName
     * @return
     * @throws Exception
     */
    @Override
    public List<String> getWorkerConfRoot(String regionName, String deployName) throws Exception {
        return jwaterService.getWorkerConfRoot(regionName, deployName);
    }

    /**
     * 获取定时任务列表
     *
     * @param regionName
     * @return
     * @throws Exception
     */
    @Override
    public List<JobInfo> getJobList(String regionName) throws Exception {
        return jwaterService.getJobList(regionName);
    }

    /**
     * 启动定时任务调度器
     *
     * @param regionName
     * @param deployName
     * @param groupName
     * @param jobName
     * @throws Exception
     */
    @Override
    public void startJob(String regionName, String deployName, String groupName, String jobName) throws Exception {
        jwaterService.startJob(regionName, deployName, groupName, jobName);
    }

    /**
     * 停止定时任务调度器
     *
     * @param regionName
     * @param deployName
     * @param groupName
     * @param jobName
     * @throws Exception
     */
    @Override
    public void stopJob(String regionName, String deployName, String groupName, String jobName) throws Exception {
        jwaterService.stopJob(regionName, deployName, groupName, jobName);
    }

    /**
     * 运行定时任务
     *
     * @param regionName
     * @param deployName
     * @param groupName
     * @param jobName
     * @param time
     * @throws Exception
     */
    @Override
    public void runJob(String regionName, String deployName, String groupName, String jobName, long time)
            throws Exception {
        jwaterService.runJob(regionName, deployName, groupName, jobName, time);
    }

    /**
     * 杀死定时任务
     *
     * @param regionName
     * @param deployName
     * @param groupName
     * @param jobName
     * @throws Exception
     */
    @Override
    public void killJob(String regionName, String deployName, String groupName, String jobName) throws Exception {
        jwaterService.killJob(regionName, deployName, groupName, jobName);
    }

    /**
     * 获取日志内容
     *
     * @param host
     * @param path
     * @return
     * @throws Exception
     */
    @Override
    public String getLogContent(String host, String path) throws Exception {
        int lineLimit = 10000;
        int lineLenLimit = 10000;
        if (useLocal && SystemMetric.isLocalHost(host)) {
            FileInputStream fis = new FileInputStream(path);
            BufferedReader br = new BufferedReader(new InputStreamReader(fis, S.charset));
            String line = null;
            LinkedList<String> lines = new LinkedList<String>();
            while ((line = br.readLine()) != null) {
                if (line.length() > lineLenLimit) {
                    line = line.substring(0, lineLenLimit) + "......";
                }
                lines.add(line);
                if (lines.size() > lineLimit) {
                    lines.removeFirst();
                }
            }
            br.close();
            StringBuilder sb = new StringBuilder();
            for (String l : lines) {
                sb.append(l).append("\r\n");
            }
            return sb.toString();
        } else {
            ProxyClient proxyClient = proxyManager.getProxyClient(host);
            if (proxyClient != null && proxyClient.isConnect()) {
                return proxyClient.getContent(path, lineLimit, lineLenLimit);
            } else {
                logger.error("proxy not online,get log content " + host + " error for " + path);
            }
        }
        return "";
    }

    /**
     * 清空日志
     *
     * @param host
     * @param path
     * @throws Exception
     */
    @Override
    public void clearLogContent(String host, String path) throws Exception {
        if (useLocal && SystemMetric.isLocalHost(host)) {
            FileUtils.write(new File(path), "");
        } else {
            ProxyClient proxyClient = proxyManager.getProxyClient(host);
            if (proxyClient != null && proxyClient.isConnect()) {
                proxyClient.saveContent(path, "");
            } else {
                logger.error("proxy not online,clear log file " + host + " error for " + path);
            }
        }
    }

    /**
     * 下载日志
     *
     * @param host
     * @param path
     * @return
     * @throws Exception
     */
    @Override
    public File downLogContent(String host, String path) throws Exception {
        if (useLocal && SystemMetric.isLocalHost(host)) {
            File srcFile = new File(path);
            File destFile = new File(HomePath.getTmpFilePath(), srcFile.getName());
            FileUtils.copyFile(srcFile, destFile);
            return destFile;
        } else {
            ProxyClient proxyClient = proxyManager.getProxyClient(host);
            if (proxyClient != null && proxyClient.isConnect()) {
                return proxyClient.getFile(path);
            } else {
                logger.error("proxy not online,down log file " + host + " error for " + path);
            }
        }
        return null;
    }

    /**
     * 获取日志列表
     *
     * @param regionName
     * @param host
     * @param dir
     * @return
     * @throws Exception
     */
    @Override
    public List<String> getHostLogList(String regionName, String host, String dir) throws Exception {
        if (useLocal && SystemMetric.isLocalHost(host)) {
            return ListLogUtils.listLog(regionName, dir);
        } else {
            ProxyClient proxyClient = proxyManager.getProxyClient(host);
            if (proxyClient != null && proxyClient.isConnect()) {
                return proxyClient.listLogFiles(regionName, dir);
            } else {
                logger.error("proxy not online,getHostLogList " + host + " error for " + regionName);
            }
        }
        return new ArrayList<String>();
    }

    /**
     * 重新加载worker、job配置文件
     *
     * @param regionName
     * @throws Exception
     */
    @Override
    public void loadConf(String regionName) throws Exception {
        jwaterService.loadConf(regionName);
    }

    /**
     * 获取主机列表
     *
     * @return
     * @throws Exception
     */
    @Override
    public List<NodeInfo> getNodeInfos() throws Exception {
        List<NodeInfo> nodeInfos = coreConfParse.getNodeList();
        return nodeInfos;
    }

    /**
     * 获取java进程列表
     *
     * @param host
     * @return
     * @throws Exception
     */
    @Override
    public List<JvmInfo> getJvmInfo(String host) throws Exception {
        if (useLocal && SystemMetric.isLocalHost(host)) {
            return JvmUtils.listJvm();
        } else {
            ProxyClient proxyClient = proxyManager.getProxyClient(host);
            if (proxyClient != null && proxyClient.isConnect()) {
                return proxyClient.listJvmInfo();
            } else {
                logger.error("proxy not online,getJvmInfo " + host + " error");
            }
        }
        return new ArrayList<JvmInfo>();
    }

    @Override
    public File downCmdJvmInfo(String host, String type, String pid) throws Exception {
        try {
            if (useLocal && SystemMetric.isLocalHost(host)) {
                String dst = JvmUtils.cmdJvm(type, Integer.parseInt(pid));
                if (dst != null) {
                    return new File(dst);
                }
            } else {
                ProxyClient proxyClient = proxyManager.getProxyClient(host);
                if (proxyClient != null && proxyClient.isConnect()) {
                    return proxyClient.cmdJvmFile(type, Integer.parseInt(pid));
                } else {
                    logger.error("proxy not online,downCmdJvmInfo " + host + " error for " + type + " " + pid);
                }
            }
        } catch (Exception e) {
            logger.error(e.toString(), e);
        }
        return null;
    }

    @Override
    public List<TreeNode> fileTreeNode(String host, String path) throws Exception {
        List<TreeNode> list = new ArrayList<>();
        if (StringUtils.isEmpty(path)) {
            String sysType = SysType.getSysType();
            String homePath = HomePath.getRemoteHomePath(coreConfParse.getNodeInfo(host).getPath()).replace("\\", "/");
            if (homePath.endsWith("/")) {
                homePath = homePath.substring(0, homePath.length() - 1);
            }
            String[] items = homePath.split("/");
            List<TreeNode> parent = list;
            String lastPath = "";
            for (String item : items) {
                TreeNode treeNode = new TreeNode();
                if (StringUtils.isEmpty(item)) {
                    treeNode.setLabel("/");
                } else {
                    treeNode.setLabel(item);
                }
                if ("/".equals(lastPath)) {
                    lastPath = "/" + item;
                } else {
                    lastPath = lastPath + "/" + item;
                }
                if ("windows".equals(sysType)) {
                    treeNode.setPath(lastPath.substring(1, lastPath.length()));
                } else {
                    treeNode.setPath(lastPath);
                }
                parent.add(treeNode);
                treeNode.setChildren(new ArrayList<>());
                parent = treeNode.getChildren();
            }
        } else {
            List<FileInfo> fileInfos = new ArrayList<>();
            if (useLocal && SystemMetric.isLocalHost(host)) {
                fileInfos = FileInfoUtils.listFileInfos(path);
            } else {
                ProxyClient proxyClient = proxyManager.getProxyClient(host);
                if (proxyClient != null && proxyClient.isConnect()) {
                    fileInfos = proxyClient.listFileInfos(path);
                } else {
                    logger.error("proxy not online,listFileInfos " + host + " error for " + path);
                }
            }
            for (FileInfo fileInfo : fileInfos) {
                if ("1".equals(fileInfo.getType())) {
                    TreeNode treeNode = new TreeNode();
                    treeNode.setLabel(fileInfo.getName());
                    treeNode.setPath(fileInfo.getPath());
                    list.add(treeNode);
                }
            }
        }
        return list;
    }

    @Override
    public List<FileInfo> fileInfoList(String host, String path) throws Exception {
        List<FileInfo> fileInfos = new ArrayList<>();
        if (useLocal && SystemMetric.isLocalHost(host)) {
            fileInfos = FileInfoUtils.listFileInfos(path);
        } else {
            ProxyClient proxyClient = proxyManager.getProxyClient(host);
            if (proxyClient != null && proxyClient.isConnect()) {
                fileInfos = proxyClient.listFileInfos(path);
            } else {
                logger.error("proxy not online,fileInfoList " + host + " error for " + path);
            }
        }
        return fileInfos;
    }

    @Override
    public void uploadFile(String host, String path, String fileName, File tmpFile) throws Exception {
        try {
            if (useLocal && SystemMetric.isLocalHost(host)) {
                File dir = new File(path);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                FileUtils.copyFile(tmpFile, new File(path, fileName));
            } else {
                String dst = path + "/" + fileName;
                ProxyClient proxyClient = proxyManager.getProxyClient(host);
                if (proxyClient != null && proxyClient.isConnect()) {
                    FileInputStream fis = new FileInputStream(tmpFile);
                    proxyClient.putFile(fis, dst);
                    IOUtils.closeQuietly(fis);
                    logger.info("upload file " + host + " success for " + fileName);
                } else {
                    logger.error("proxy not online,upload file " + host + " error for " + fileName);
                }
            }
        } finally {
            FileUtils.deleteQuietly(tmpFile);
        }
    }

    @Override
    public void deleteFile(String host, String path) throws Exception {
        if (useLocal && SystemMetric.isLocalHost(host)) {
            boolean success = FileUtils.deleteQuietly(new File(path));
            if (!success) {
                throw new RuntimeException("删除文件失败" + path);
            }
        } else {
            ProxyClient proxyClient = proxyManager.getProxyClient(host);
            if (proxyClient != null && proxyClient.isConnect()) {
                proxyClient.removeFile(path);
                logger.info("delete to " + host + " success for " + path);
            } else {
                logger.error("proxy not online,delete file " + host + " error for " + path);
            }
        }
    }

    @Override
    public List<SystemInfo> getAllSystemInfo() throws Exception {
        return jwaterService.getAllSystemInfo();
    }

    @Override
    public void removeAlarmInfo(String type) {
        jwaterService.removeAlarmInfo(type);
    }

    @Override
    public List<AlarmInfo> getAllAlarmInfo() {
        return jwaterService.getAllAlarmInfo();
    }

    @Override
    public List<PackageInfo> getPackageInfo() {
        return jwaterService.getPackageInfo();
    }

    @Override
    public List<DeployInfo> getDeployInfo(String regionName) throws Exception {
        List<DeployInfo> list = new ArrayList<>();
        List<String> deployNames = HomePath.getDeployNames(regionName);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (String deployName : deployNames) {
            DeployInfo deployInfo = new DeployInfo();
            deployInfo.setDeployName(deployName);
            ParamMap infoProps = WorkerConfParse.parseInfo(regionName, deployName);
            Long version = null;
            if (infoProps != null) {
                version = infoProps.getLong("version");
                deployInfo.setPath(infoProps.getString("path", ""));
            }
            File file = DeployPathUtils.getDeployPath(regionName, deployName, version);
            if (file != null) {
                deployInfo.setVersion(Long.parseLong(file.getName()));
            }
            if (deployInfo.getVersion() != 0) {
                deployInfo.setVersionDate(sdf.format(new Date(deployInfo.getVersion())));
            }
            list.add(deployInfo);
        }
        return list;
    }

    @Override
    public void createDeployDir(String regionName, String deployName) throws Exception {
        File dir = new File(HomePath.getRegion() + regionName + "/" + deployName);
        if (!dir.exists()) {
            new File(dir, System.currentTimeMillis() + "").mkdirs();
        }
    }

    @Override
    public List<String> getDeployVersionList(String regionName, String deployName) throws Exception {
        List<String> list = new ArrayList<>();
        File dir = new File(HomePath.getRegionDeploy(regionName) + deployName);
        if (dir.exists()) {
            File[] files = dir.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        list.add(file.getName());
                    }
                }
            }
        }
        return list;
    }

    @Override
    public void changeDeployProp(String regionName, String deployName, String key, String value) throws Exception {
        List<NodeInfo> nodeInfos = coreConfParse.getNodeList();
        for (NodeInfo nodeInfo : nodeInfos) {
            String host = nodeInfo.getHost();
            if (useLocal && SystemMetric.isLocalHost(host)) {
                DeployUtils.updateInfoProp(regionName, deployName, key, value);
            } else {
                ProxyClient proxyClient = proxyManager.getProxyClient(host);
                if (proxyClient != null && proxyClient.isConnect()) {
                    proxyClient.changeDeployProp(regionName, deployName, key, value);
                    logger.info("changeDeployProp " + host + " success for " + regionName + "," + deployName);
                } else {
                    logger.error("proxy not online,changeDeployProp " + host + " error for " + regionName + "," + deployName);
                }
            }
        }
    }

    @Override
    public List<GatewayProxyInfo> getGatewayProxyInfo(String gatewayGroupName, String gatewayWorkerName, int gatewayIndex, String gatewayRegionName, String gatewayDeployName) {
        return jwaterService.getGatewayProxyInfo(gatewayGroupName, gatewayWorkerName, gatewayIndex, gatewayRegionName, gatewayDeployName);
    }
}
